盒子
盒子
文章目录
  1. StrictMode
  2. 案例
  3. 参考文献

Android 性能优化:StrictMode

StrictMode

Android 2.3 引入了 android.os.StrictMode(Android 3.0 中又在 StrictMode 中新加入了几个方法),为了能够帮助开发者检测 “主线程” 或 “虚拟机” 的一些影响性能或者不良的代码,为了能够让应用更平滑、响应更快。当然如果你已经足够了解 android 最佳实践,完全可以忽略这个类。注意:这个类只在开发时使用,发布时请去除。

目前 Android 4.0 以上的占有率为 84.3%,因此有很多应用直接 minSdk 设置为 14。因此一般不必考虑 StrictMode 中 API Level 的问题。

StrictMode 共有两种策略(policy):

  • ThreadPolicy: 线程相关策略,包括主线程访问网络、磁盘(现在手机中使用闪存)读写、慢代码的检测。我们能够分别检测(detect)这些操作或允许(permit)这些操作。一旦出现了违规(violation),就会有相应的提示(比如 Log 显示)。
  • VMPolicy: 虚拟机相关的策略,包括 SQLite 或 SQLiteCursor 没关闭、实现 Closable 接口的类使用后没关闭 等。

当然也可以忽略某些违规,比如检测磁盘读写,因为一般来说在主线程中进行文件系统的读写是可以的。

一旦我们在入口 Activity 的 onCreate() 的最前面添加 StrictMode 相关代码,则在整个程序中 StrictMode 都会有效。

模版代码:

protected void onCreate(Bundle savedInstanceState) {
if (DEVELOPER_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate(savedInstanceState);
}

当然,为了方便,可以直接使用 StrictMode.enableDefaults() 启用 StrictMode。

其实 ThreadPolicy 和 VMPolicy 差不多,detectAll() 表示检测全部的违规,penaltyLog() 表示当违规时打印 Log。

  • 对于 ThreadPolicy 来说,我们可以使用 detectNetwork() 检测主线程网络访问,detectDiskReads()detectDiskWrites() 检测主线程磁盘读写,detectCustomSlowCalls() 检测主线程自定义的慢代码。当然也可以使用 permitXXX() 允许这些操作。
  • 对于 VMPolicy 来说,detectLeakedSqliteObjects() 检测 SQLite 和 SQLiteCursor 内存泄漏(没关闭),detectLeakedClosableObjects() 检测实现 Closable 接口的对象内存泄漏。
  • 我们能通过 StrictMode.getThreadPolicy()StrictMode.getVMPolicy() 获得当前采取的 ThreadPolicy 和 VMPolicy。

案例

场景:从网络上下载一张图片并且在 ImageView 中显示。

核心代码如下:

ImageView imageView = (ImageView)findViewById(R.id.view);
URL url = null;
Bitmap bmp = null;
try {
url = new URL("http://www.tencent.com/1.jpg");
bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
}catch(Exception e){
e.printStackTrace();
}finally {
if(bmp!=null){
imageView.setImageBitmap(bmp);
}
}

这段代码一运行,StrictMode 就出现了一堆建议:

1、 根据下面的 log 可以看出查找域名花费了 446 ms。

D/StrictMode﹕ StrictMode policy violation; ~duration=446 ms: android.os.StrictMode$StrictModeNetworkViolation: policy=63 violation=4
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1184)
at java.net.InetAddress.lookupHostByName(InetAddress.java:394)
...
at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:168)
at info.xiazdong.performanceoptimization.MainActivity.onCreate(MainActivity.java:33)
...

2、连接服务器花费 377 ms。

D/StrictMode﹕ StrictMode policy violation; ~duration=377 ms: android.os.StrictMode$StrictModeNetworkViolation: policy=63 violation=4
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1184)
at libcore.io.BlockGuardOs.connect(BlockGuardOs.java:84)
at libcore.io.IoBridge.connectErrno(IoBridge.java:127)
at libcore.io.IoBridge.connect(IoBridge.java:112)
...
at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:168)
at info.xiazdong.performanceoptimization.MainActivity.onCreate(MainActivity.java:36)
...

3、 解码图片花费了 205 ms。

D/StrictMode﹕ StrictMode policy violation; ~duration=205 ms: android.os.StrictMode$StrictModeNetworkViolation: policy=63 violation=4
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1184)
at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:163)
at libcore.io.IoBridge.recvfrom(IoBridge.java:503)
at java.net.PlainSocketImpl.read(PlainSocketImpl.java:488)
...
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:791)
at info.xiazdong.performanceoptimization.MainActivity.onCreate(MainActivity.java:36)
...

因此访问网络的操作都建议在子线程中进行,绝对不能在主线程进行。

现在手机的像素越来越高,魅族4的相机像素达到 2070 万,拍出照片的尺寸是 5248 3936,如果这张照片是 ARGB_8888 类型的位图,则放在内存中将占据:2070 4 字节,约 78M,即使手机有足够内存放这张图片,在主线程解码(decode)也需要花费不少时间,因此一般这种耗时的操作都在子线程中进行,比如使用 AsyncTask(因为屏幕像素和相机像素差距比较大,因此一般都不会原图显示,因为手机上显示一张高清图片没有什么价值)

参考文献

支持一下
扫一扫,支持xiazdong